package presentacion;

/*Codigo creado por:
- Francisco Ruiz Romero 			05715573G
- Rosana Rguez-Bobada Aranda		70592974V
Escuela Superior de Informática(ESI) - UCLM
*/

import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
import java.util.Scanner;

import dominio.Estado;
import dominio.Frontera;
import dominio.NodoBusqueda;
import dominio.Util;
import dominio.Problema;


public class Principal {
	static long nodos_insertados;
	public static void main(String args[]) throws Exception {
		
		//Declaración e inicialización de variables.
		String fname = "Datos.h5";
		String[] vecHojas = { "Archivos/MDT25-0711-H30.asc", "Archivos/MDT25-0712-H30.asc","Archivos/MDT25-0736-H30.asc", "Archivos/MDT25-0737-H30.asc" };
		int estrategiaSelect=0, profMax;
		List<NodoBusqueda> solucion; /* Lista que contendria la solucion, es decir, la secuencia.*/
		Scanner leer =new Scanner(System.in);
		Problema p;
		long tiempo_inicial,tiempo_final;
		String estrategia;
		//Para más tarde generar el fichero
		FileWriter fichero =new FileWriter("prueba.txt");
	    PrintWriter pw = new PrintWriter(fichero);
		
		//Creamos el archivo H5.
		Util.createFile(fname, vecHojas);
		//Creación del problema 
		p=recogerDatosProblema();
		do{
			System.out.printf("Introduzca el número correspondiente a la estrategia deseada: \n 1) Voraz \n 2) A \n 3) Anchura \n 4) Profundidad \n 5) Uniforme \n");
			estrategiaSelect=leer.nextInt();
		}while(estrategiaSelect<1 || estrategiaSelect>5);
		
		System.out.println("Introduzca la profundidad máxima a la que desea expandir la búsqueda:");
		profMax=leer.nextInt();
		estrategia=accionSelect(estrategiaSelect);
		
		boolean optimizacion=true;
		if(optimizacion){
			Util.generarMatricesOptimizacion(p);
		}
		//Busqueda de solución según la estrategia seleccionada y la profundidad máxima
		tiempo_inicial=System.currentTimeMillis();
		solucion=Busqueda(p,estrategia,profMax,optimizacion);
		tiempo_final=System.currentTimeMillis();
		
		//Imprimir solución
		if(!solucion.isEmpty()){
			imprimirSolucion(solucion,pw,tiempo_final-tiempo_inicial,estrategia);
		}else{
			imprimirNoSolucion(pw,tiempo_final-tiempo_inicial,estrategia,profMax);
			}
		System.out.println("Ha terminado la ejecución del programa, los resultados se encuentran en el archivo: Solucion.txt");
		fichero.close();
		leer.close();
	}
	
	
	public static List<NodoBusqueda> Busqueda(Problema p,String e,int max_prof,boolean optimizacion) throws Exception{
		Frontera f=new Frontera();
		boolean haySolucion;
		List<Estado> LS;
		List<NodoBusqueda> LN;
		NodoBusqueda nodo_actual = null;
		List<NodoBusqueda> solucion = new ArrayList<NodoBusqueda>();
		NodoBusqueda nodo_inicial=new NodoBusqueda(0,null,p.getEstInicial(),"",0,false,0);
		
		f.insertarNodo(nodo_inicial);
		haySolucion=false;
		while(!haySolucion && !f.esVacia()){
			nodo_actual=f.seleccionarNodo();
			//nodo_actual.getEstado().imprimirEstado();
			if(p.Meta(nodo_actual.getEstado())){
				haySolucion=true;
			}else{
				if(nodo_actual.getProf()<max_prof){
					LS=Util.Sucesor(nodo_actual.getEstado());
					//Collections.shuffle(LS);//Para no seguir siempre el mismo orden al insertar la frontera (sesgo)	
					LN=Util.CrearListaNodosArbol(LS, e, nodo_actual,p,optimizacion);
					f.insertarLista(LN);
					
				}
			}
		}
		if(haySolucion){
			solucion=Util.crearSolucion(nodo_actual);
		}
		nodos_insertados=f.nodosInsertados();
		
		return solucion;	
	}
	
	public static void imprimirNoSolucion(PrintWriter pw,long tiempo,String e,int profMax){
		long minutos,segundos,milisegundos;
		minutos=tiempo/60000;
		segundos=(tiempo-minutos*60000)/1000;
		milisegundos=tiempo-(minutos*60000+segundos*1000);
		pw.println("No se ha encontrado ninguna solución para profundidad "+profMax+" y estrategia "+e+".");
		pw.println("Tiempo total: "+minutos+" minutos "+segundos+" segundos "+milisegundos+" milisegundos.");
		pw.println("Numero total de nodos insertados: "+nodos_insertados);
	}
	
	public static void imprimirSolucion(List<NodoBusqueda> s,PrintWriter pw,long tiempo,String e){
		NodoBusqueda n = null;
		long minutos,segundos,milisegundos;
		minutos=tiempo/60000;
		segundos=(tiempo-minutos*60000)/1000;
		milisegundos=tiempo-(minutos*60000+segundos*1000);
		//pw.println("SOLUCION usando la estrategia "+e+".");
		//pw.println();
		n=s.get(0);
		pw.println(n.getCosto()+" "+n.getProf()+" "+minutos+":"+segundos+":"+milisegundos);
		for(int i=s.size()-1;i>0;i--){
			n=s.get(i);
			pw.println(n.getEstado().imprimirEstado());
		}
		//pw.println("Profundidad de la solución: ");
		//pw.println("Tiempo total: "+minutos+" minutos "+segundos+" segundos "+milisegundos+" milisegundos.");
		//pw.println("Numero total de nodos insertados: "+nodos_insertados);
		/*	while(!s.isEmpty()){
		n=s.pop();
		pw.println("ID: "+n.getID()+". ID_Padre: "+n.getPadre().getID()+". Acción: "+n.getAcc()+". Costo: "+n.getCosto());
	}*/
		
	}
	
	public static Problema recogerDatosProblema() throws Exception{
		int eOx, eOy, eDx, eDy;
		int[] dato;
		@SuppressWarnings("resource")
		Scanner leer =new Scanner(System.in);
		
		//Capturamos las coordenadas de los puntos.
			System.out.println("Introduzca las coordenadas de los estados.");
			//Datos del estado inicial.
			System.out.println("Estado inicial, coordenada X:");
			eOx=Integer.parseInt(leer.nextLine());
			System.out.println("coordenada Y:");
			eOy=Integer.parseInt(leer.nextLine());
			dato=Util.lugarGeografico(eOx,eOy);
			double altitud=Util.altitud(dato[0],dato[1],dato[2]);
			//Creamos el estado inicial.
			Estado e=new Estado(dato[0],dato[1],dato[2],altitud,"");
			//Datos del estado destino.
			System.out.println("Estado destino, coordenada X:");
			eDx=Integer.parseInt(leer.nextLine());
			System.out.println("coordenda Y:");
			eDy=Integer.parseInt(leer.nextLine());
			dato=Util.lugarGeografico(eDx, eDy);
			altitud=Util.altitud(dato[0],dato[1],dato[2]);
			//Creamos el estado destino.
			Estado ef=new Estado(dato[0],dato[1],dato[2],altitud,"");
	
		return new Problema(null, e, ef);
	}
	
	public static String accionSelect(int accRecogida){
		String accion="";
		switch(accRecogida){
		case 1:
			accion="voraz";
			break;
		case 2: 
			accion="A";
			break;
		case 3:
			accion="anchura";
			break;
		case 4:
			accion="profundidad";
			break;
		case 5:
			accion="uniforme";
			break;
		}
		return accion;
	}
	
	public static void imprimirEstadisticas(PrintWriter pw,long nodos, long tMAX, long tMIN, double tMED,int opt){
		if(opt==1){
			pw.println("\tESTADISTICAS FINALES: ");
		}
		pw.println("");
		pw.println("Nodos insertados: "+nodos);
		pw.println("Tiempo MAX: "+tMAX);
		pw.println("Tiempo MIN: "+tMIN);
		pw.println("Tiempo MEDIO: "+tMED);
		pw.println("--------------------------------");
	}
	
	public static void imprimirSucesores(List<Estado> LS){
		System.out.println("Sucesores: ");
		for(int i=0;i<LS.size();i++){
			System.out.println(LS.get(i).getHoja()+" "+LS.get(i).getX()+" "+LS.get(i).getY()+" "+LS.get(i).getAltitud()+" "+LS.get(i).getAcc());
			
		}
	}
	
	public static void imprimirEstado(PrintWriter pw,Estado e){
		pw.println("Voy a expandir: ");
		pw.println(e.getHoja()+" "+e.getX()+" "+e.getY()+" "+e.getAltitud()+" "+e.getAcc());
	}
	
	
	
		
	
}